﻿using System;
using System.Diagnostics;
using System.IO;

namespace SMOperator
{
    enum ReturnCode : int
    {
        Success = 0,
        WarningGraceful = -1,
        FailDueSetup = -2,
        FailDueArgument = -3,
        Fail = -10
    }
    class Program
    {
        static int Main(string[] args)
        {
            if (args.Length == 0)
            {
                Trace.WriteLine("Missing arguments");
                PrintArguments();
                return (int)ReturnCode.FailDueArgument;
            }

            DateTime startDate = DateTime.Now;

            string mode = string.Empty;
            string logicalName = "Database Source";
            string connectionName = string.Empty;

            foreach (string arg in args)
            {
                if(arg.Contains("?"))
                {
                    PrintArguments();
                    return (int)ReturnCode.FailDueArgument;
                }
                switch (arg.Substring(0, arg.IndexOf("=")).ToLower())
                {
                    case "mode":
                        mode = ParameterValue(arg) != string.Empty ? ParameterValue(arg) : string.Empty;
                        break;
                    case "connectionname":
                        connectionName = ParameterValue(arg) != string.Empty ? ParameterValue(arg) : string.Empty;
                        break;
                    case "logicalname":
                        logicalName= ParameterValue(arg) != string.Empty ? ParameterValue(arg) : string.Empty;
                        break;
                    default:
                        break;
                }
            }

            if (string.IsNullOrEmpty(mode) || string.IsNullOrEmpty(connectionName) || string.IsNullOrEmpty(logicalName))
            {
                PrintArguments(); return (int)ReturnCode.FailDueArgument;
            }

            int warnings = 0;

            RemoveErrorLog();
            string baseCatalogPath = SMOperator.SetupPath(logicalName);

            try
            {                  
                switch (mode)
                {
                    case "to":
                        {
                            SMOperator op = new SMOperator();
                            warnings = op.ScriptToCatalog(connectionName, logicalName);
                            break;
                        }
                    case "from":
                        {
                            SMOperator op = new SMOperator();
                            warnings = op.ScriptFromCatalog(connectionName, logicalName);
                            break;
                        }
                    case "index":
                        {
                            SMOperator op = new SMOperator();
                            warnings = op.IndexCatalog(connectionName, logicalName, true, false);
                            break;
                        }
                    case "deindex":
                        {
                            SMOperator op = new SMOperator();
                            warnings = op.IndexCatalog(connectionName, logicalName, false, false);
                            break;
                        }
                    case "deindexall":
                        {
                            SMOperator op = new SMOperator();
                            warnings = op.IndexCatalog(connectionName, logicalName, false, true);
                            break;
                        }
                    default:
                        SMOperator.InvalidArgs(); PrintArguments(); break;
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine(e.Message);
                return (int)ReturnCode.Fail;
            }

            TimeSpan delta = DateTime.Now.Subtract(startDate);
            Trace.WriteLine(string.Format("Completed in {0}", delta.ToString()));

            //no warnings
            if (warnings == 0)
                return (int)ReturnCode.Success;
            //failed due to setup
            else if (warnings < 0)
            {
                return warnings;
            }
            //finished with warnings
            else if (warnings > 1)
                return (int)ReturnCode.WarningGraceful;
            else
                return (int)ReturnCode.Fail;            
        }

        private static void RemoveErrorLog()
        {
            try
            {
                string errorPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName), "SmoErrorLog.xml");
                if (File.Exists(errorPath))
                {
                    DateTime createdDate = File.GetCreationTime(errorPath);
                    string newFileName = string.Format("SmoErrorLog-{0}.xml", createdDate.ToString("yyyyMMdd-hhMMss"));

                    if (File.Exists(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName), newFileName)))
                        File.Delete(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName), newFileName));

                    File.Move(errorPath, Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName), newFileName));
                }
            }
            catch
            {
                File.Delete(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName), "SmoErrorLog.xml"));
            }
        }
        static void PrintArguments()
        {
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Valid Arguments:");
            Console.WriteLine("----------------------------------");
            Console.WriteLine("     mode=from connectionName=name logicalname=name    Scripts a db/catalog to file");
            Console.WriteLine("     mode=to connectionName=name logicalname=name      Recreates a db/catalog from file");
            Console.WriteLine();
            Console.WriteLine("Mandatory Arguments:");
            Console.WriteLine("     mode={mode}                              Name of operation: to / from");
            Console.WriteLine();
            Console.WriteLine("Additional Arguments:");
            Console.WriteLine("     connectionName={app.config.name}        Name of connection string in app.config");
            Console.WriteLine("     logcalName=\"{logical name}\"           Name of catalog");
            Console.WriteLine();
            Console.WriteLine("Valid Modes:");
            Console.WriteLine("     to                                      Scripts all objects from source to a catalog");
            Console.WriteLine("     from                                    Scripts all objects from a catalog to source");
            Console.WriteLine("     index                                   Executes all index scripts into a database");
            Console.WriteLine("     queryplan                               Executes queryplan-to-index scripting in a database");
            Console.WriteLine("     testplan                                Executes all non-table objects in a database ");
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Example FROM:");
            Console.WriteLine("                 SMOperator mode=from connectionName=PreStaging logicalName=\"Staging Source\"");
            Console.WriteLine("Example TO:");
            Console.WriteLine("                 SMOperator mode=to connectionName=PreStaging logicalName=\"Staging Source\"");
            Console.WriteLine("Example INDEX:");
            Console.WriteLine("                 SMOperator mode=index connectionName=PreStaging logicalName=\"Staging Source\"");
            Console.WriteLine("Example QUERYPLAN:");
            Console.WriteLine("                 SMOperator mode=queryplan connectionName=PreStaging logicalName=\"Staging Source\"");
            Console.WriteLine("Example TESTPLAN:");
            Console.WriteLine("                 SMOperator mode=testplan connectionName=PreStaging logicalName=\"Staging Source\"");
        }
        #region Internal
        protected static int ParameterValueAsInt(string tokenizedParameter)
        {
            int tmp = 0;
            int.TryParse(ParameterValue(tokenizedParameter), out tmp);
            if (tmp > 0)
                return tmp;
            else
            {
                Trace.WriteLine(string.Format("Incorrect parameter '{0}', value must be numeric and seperated by '=', ex: 'param=4'"));
                Console.ReadKey();
                return -1;
            }
        }
        protected static string ParameterValue(string tokenizedParameter)
        {
            if (!tokenizedParameter.Contains("="))
                return string.Empty;

            string value = tokenizedParameter.Substring(tokenizedParameter.IndexOf("=") + 1, tokenizedParameter.Length - (tokenizedParameter.IndexOf("=") + 1));

            return value;
        }
        #endregion
    }
    class ConsoleSpiner
    {
        int counter;
        public ConsoleSpiner()
        {
            counter = 0;
        }
        public void Turn()
        {
            counter++;
            switch (counter % 4)
            {
                case 0: Console.Write("/"); break;
                case 1: Console.Write("-"); break;
                case 2: Console.Write("\\"); break;
                case 3: Console.Write("-"); break;
            }
            Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
        }
        public void TurnWithValue(string value)
        {
            counter++;
            string turn = string.Empty;
            switch (counter % 4)
            {
                case 0:
                    turn = string.Format("/ {0}", value);
                    Console.Write(turn); 
                    break;
                case 1: 
                    turn = string.Format("- {0}", value);
                    Console.Write(turn); 
                    break;
                case 2:
                    turn = string.Format(@"\ {0}", value);
                    Console.Write(turn); 
                    break;
                case 3:
                    turn = string.Format("| {0}", value);
                    Console.Write(turn); 
                    break;
            }
            if((Console.CursorLeft - turn.Length) >= 0)
                Console.SetCursorPosition( Console.CursorLeft - turn.Length, Console.CursorTop);
        }
    }
}

